﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Shell;
using Microsoft.Phone.Controls;
using GeoFinderClientApp.ViewModel;
using GeoFinderClientApp.Model;
using GeoFinderClientApp.Web;
using Microsoft.Maps.MapControl;
using System.Device.Location;
using GeoFinderClientApp.GeocodeService;
using System.ServiceModel;
using System.Windows.Browser;
using GeoFinderClientApp.Utils.ReverseGeoCoord;


namespace GeoFinderClientApp.View
{
    public partial class ApplicationsMain : PhoneApplicationPage
    {


        ApplicationBarMenuItem roadMode, aerialMode;
        ApplicationBarIconButton ZoomIn, ZoomOut, refresh, settings;
        ViewModels vm;


        public ApplicationsMain()
        {
            InitializeComponent();

            WebCore.FacebookDataRecived += new EventHandler<LoginEventArgs>(WebCore_FacebookDataRecived);
            WebCore.FacebookFailure += new EventHandler<LoginEventArgs>(WebCore_FacebookFailure);

            aerialMode = ApplicationPage.ApplicationBar.MenuItems[0] as ApplicationBarMenuItem;
            roadMode = ApplicationPage.ApplicationBar.MenuItems[1] as ApplicationBarMenuItem;
            ApplicationPage.ApplicationBar.MenuItems.Clear();
            ZoomIn = ApplicationPage.ApplicationBar.Buttons[0] as ApplicationBarIconButton;
            ZoomOut = ApplicationPage.ApplicationBar.Buttons[1] as ApplicationBarIconButton;
            refresh = ApplicationPage.ApplicationBar.Buttons[2] as ApplicationBarIconButton;
            settings = ApplicationPage.ApplicationBar.Buttons[3] as ApplicationBarIconButton;
            ApplicationPage.ApplicationBar.Buttons.Clear();

            vm = new ViewModels();
            vm.GetMe();
            SetMyPositionAndArea();

            map.Culture = "IT-it";

            map.ZoomLevel = 15;

        }

        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            if (vm == null)
            {
                vm = new ViewModels();
            }

            vm.GetFriends();
            vm.GetMe();
            vm.GetSearchingAreaOptions();

            var searching = vm.SearchingAreaOptions[0].SearchingArea;
            

            FL.DataContext = vm.AllFriends.OrderBy(f => f.Name);

            var whiteList = vm.AllFriends.Where(f => f.IsWhiteVisible).ToList();

            WhiteListed.DataContext = whiteList;
            WhiteListed2.DataContext = vm.AllFriends;

            TotalFriends.DataContext = vm.AllFriends;
            try
            {
                NearFriends.DataContext = (from f in vm.AllFriends where f.Position.GetDistanceTo(vm.Me.Position) <= searching select f).ToList<Friend>();
            }
            catch
            {
                NearFriends.DataContext = new List<Friend>();
            }

            PlaceFriends();

            vm.GetMe();
            var mypos = new Location(vm.Me.Position.Latitude, vm.Me.Position.Longitude);
            ReverseGeocodeLocation(mypos);
            map.Center = mypos;
            SetMyPositionAndArea();
                       
        }

        private void PlaceFriends()
        {
            MapOther.Children.Clear();
            foreach (var item in vm.AllFriends)
            {
                var element = CreateAUIElement(item.Name);
                Location location = new Location();
                if (item.Position != null)
                {
                    location = new Location(item.Position.Latitude, item.Position.Longitude);
                }
                PositionOrigin position = PositionOrigin.Center;
                MapOther.AddChild(element, location, position);
                MapLayer.SetPositionOffset(element, new Point());
            }
        }


        #region reverseGeoCode

        private GeocodeServiceClient geocodeClient;
        private GeocodeServiceClient GeocodeClient
        {
            get
            {
                if (null == geocodeClient)
                {
                    //Handle http/https; OutOfBrowser is currently supported on the MapControl only for http pages
                    bool httpsUriScheme = true;//HtmlPage.Document.DocumentUri.Scheme.Equals(Uri.UriSchemeHttps);
                    BasicHttpBinding binding = new BasicHttpBinding(httpsUriScheme ? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.None);
                    UriBuilder serviceUri = new UriBuilder("http://dev.virtualearth.net/webservices/v1/GeocodeService/GeocodeService.svc");
                    if (httpsUriScheme)
                    {
                        //For https, change the UriSceheme to https and change it to use the default https port.
                        serviceUri.Scheme = Uri.UriSchemeHttps;
                        serviceUri.Port = -1;
                    }

                    //Create the Service Client
                    geocodeClient = new GeocodeServiceClient(binding, new EndpointAddress(serviceUri.Uri));
                    geocodeClient.ReverseGeocodeCompleted += new EventHandler<ReverseGeocodeCompletedEventArgs>(client_ReverseGeocodeCompleted);
                }
                return geocodeClient;
            }
        }

        private GeocodeLayer geocodeLayer;
        private GeocodeLayer GeocodeLayer
        {
            get
            {
                if (null == geocodeLayer)
                {
                    geocodeLayer = new GeocodeLayer(map);
                }
                return geocodeLayer;
            }
        }

        // Call service to do reverse geocode ... async call.
        private void ReverseGeocodeAsync(Location location)
        {
            ReverseGeocodeRequest request = new ReverseGeocodeRequest();
            request.Culture = map.Culture;
            request.Location = new Location();
            request.Location.Latitude = location.Latitude;
            request.Location.Longitude = location.Longitude;
            // Don't raise exceptions.
            request.ExecutionOptions = new ExecutionOptions();
            request.ExecutionOptions.SuppressFaults = true;

            geocodesInProgress++;

            map.CredentialsProvider.GetCredentials(
                (Credentials credentials) =>
                {
                    //Pass in credentials for web services call.
                    //Replace with your own Credentials.
                    request.Credentials = credentials;

                    // Make asynchronous call to fetch the data ... pass state object.
                    GeocodeClient.ReverseGeocodeAsync(request, location);
                });
        }

        // Keep track of addresses already found.
        private Dictionary<string, bool> found = new Dictionary<string, bool>();

        private void client_ReverseGeocodeCompleted(object sender, ReverseGeocodeCompletedEventArgs e)
        {
            // Finished.
            lock (waitingToReverseGeocode)
            {
                geocodesInProgress--;
            }

            Location location = (Location)e.UserState;
            string outputString = "";//string.Format("Location ({0:f6}, {1:f6}) : ", location.Latitude, location.Longitude);

            try
            {
                if (e.Result.ResponseSummary.StatusCode != ResponseStatusCode.Success)
                {
                    PositionBox.Text = "error geocoding ... status <" + e.Result.ResponseSummary.StatusCode.ToString() + ">";
                }
                else if (0 == e.Result.Results.Count)
                {
                    PositionBox.Text = outputString + "No results";
                }
                else
                {
                    string formatted = e.Result.Results[0].DisplayName;
                    outputString = outputString + formatted;
                    outputString = returnVirgola(outputString);
                    if (found.ContainsKey(formatted))
                    {
                        PositionBox.Text = outputString;
                    }
                    else
                    {
                        found[formatted] = true;
                        PositionBox.Text = outputString; //+ "  (" + e.Result.Results[0].Locations[0].CalculationMethod + ")";
                        GeocodeLayer.AddResult(e.Result.Results[0]);
                    }

                }
            }
            catch (Exception)
            {
                PositionBox.Text= "Exception raised calling reverse geocoder";
            }

            // See if there are more waiting to run.
            ReverseGeocodeFromQueue();
        }

        private string returnVirgola(string outputString)
        {
            try
            {
                var rows = outputString.Split(new char[] { ',' });

                var acc = "";
                foreach (var item in rows)
                {
                    acc += item + "\n";
                }
                return acc;
            }
            catch { return outputString; }
        }

        // Throttle calls on geocoding service.
        private const int MaxGeocodes = 3;

        // Locations waiting to be reverse geocoded.
        private Queue<Location> waitingToReverseGeocode = new Queue<Location>();

        // Waiting for results from the server for this many.
        int geocodesInProgress = 0;
        bool Geocoding
        {
            get { return geocodesInProgress > 0; }
        }

        // Runs as many reverse geocodes as it can from the queue.
        private void ReverseGeocodeFromQueue()
        {
            lock (waitingToReverseGeocode)
            {
                while (geocodesInProgress < MaxGeocodes && waitingToReverseGeocode.Count > 0)
                {
                    ReverseGeocodeAsync(waitingToReverseGeocode.Dequeue());
                }
            }
        }

        private void ReverseGeocodeLocation(Location location)
        {
            // All calls go through the queue.
            lock (waitingToReverseGeocode)
            {
                waitingToReverseGeocode.Enqueue(location);
                ReverseGeocodeFromQueue();
            }
        }

        private void ReverseGeocodeLocation(IList<Location> locations)
        {
            lock (waitingToReverseGeocode)
            {
                foreach (Location location in locations)
                {
                    waitingToReverseGeocode.Enqueue(location);
                }
                ReverseGeocodeFromQueue();
            }
        }

        #endregion

        #region Other Methods

        private void SetMyPositionAndArea()
        {
            vm.GetMe();
            vm.GetSearchingAreaOptions();
            var myloc = new Location(vm.Me.Position.Latitude, vm.Me.Position.Longitude);
            MapLayer.SetPosition(nipple, myloc);

            if (SearchingArea.Children.Count > 0)
            {
                SearchingArea.Children.Clear();
            }

            AddSearchingAreaPolygon(vm.SearchingAreaOptions[0].SearchingArea, myloc);
        
        }

        private void AddSearchingAreaPolygon(double ray, Location location)
        {
            double centerX = location.Latitude;
            double centerY = location.Longitude;
            int pointsCount = 50;
            double thickness = 1;
            Color color = Color.FromArgb(200, 255, 255, 255);
            Color fillColor = Color.FromArgb(70, 255, 0, 0);
            
            LocationCollection points = GetPoints(pointsCount, location, ray);

            MapPolygon shape = new MapPolygon();
            shape.Locations = points;
            shape.StrokeThickness = thickness;
            shape.Stroke = new SolidColorBrush(color);
            shape.Fill = new SolidColorBrush(fillColor);

            //MapLayer.SetPosition(shape, location);

            SearchingArea.Children.Add(shape);
        }

        private double distance(Point p1, Point p2)
        {
            var dx2 = Math.Pow(p1.X - p2.X, 2);
            var dy2 = Math.Pow(p1.Y - p2.Y, 2);
            return Math.Sqrt(dx2 + dy2);
        }

        private LocationCollection GetPoints(int count, Location myLoc, double ray)
        {
            Point center = map.LocationToViewportPoint(myLoc);

            var nloc = new Location(myLoc.Latitude + (ray / 1000) * 1.0 / 111.132, myLoc.Longitude);
            double width = distance(center, map.LocationToViewportPoint(nloc));



            LocationCollection points = new LocationCollection();
            for (int i = 0; i < count; i++)
            {
                double j = (double)i / (double)count;
                Location location;
                if (map.TryViewportPointToLocation(
                    new Point(center.X + Math.Cos(j * 2.0 * Math.PI) * width,
                              center.Y + Math.Sin(j * 2.0 * Math.PI) * width), out location))
                {
                    points.Add(location);
                }
            }
            return points;
        }

        private UIElement CreateAUIElement(string name)
        {
            
            Canvas canvas = new Canvas();
            canvas.Background = new SolidColorBrush(Colors.Blue);
            canvas.Opacity = .7;
            canvas.Width = 50;
            canvas.Height = 80;
            
            TextBlock tb = new TextBlock();
            var txt = name.Split(new char[]{' '});
            var text = "";
            foreach (var item in txt)
            {
                text += item.Substring(0, Math.Min(5, item.Length)) + "..\n";
            }

            tb.Text = text;
            tb.FontSize = 10;
            tb.Foreground = new SolidColorBrush(Color.FromArgb(255,255,255,255));
            tb.Margin = new Thickness(5);
            tb.TextAlignment = TextAlignment.Center;


            canvas.Children.Add(tb);

            return canvas;
        }

        #endregion

        #region Events Handler

        private void ManageFriends_Click(object sender, RoutedEventArgs e)
        {
            NavigationService.Navigate(new Uri("/View/FriendsManager.xaml", UriKind.Relative));
        }

        private void Aerial_Click(object sender, EventArgs e)
        {
            map.Mode = new Microsoft.Maps.MapControl.AerialMode(true);  
        }

        private void Road_Click(object sender, EventArgs e)
        {
            map.Mode = new Microsoft.Maps.MapControl.RoadMode();
        }
                
        private void PivotController_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ApplicationPage.ApplicationBar.Buttons.Clear();
            ApplicationPage.ApplicationBar.MenuItems.Clear();

            switch (PivotController.SelectedIndex)
            {
                case 0:
                    ApplicationPage.ApplicationBar.Buttons.Add(refresh);
                    ApplicationPage.ApplicationBar.Buttons.Add(settings);
                    break;
                case 1:
                    ApplicationPage.ApplicationBar.Buttons.Add(ZoomIn);
                    ApplicationPage.ApplicationBar.Buttons.Add(ZoomOut);
                    ApplicationPage.ApplicationBar.Buttons.Add(refresh);
                    ApplicationPage.ApplicationBar.Buttons.Add(settings);
                    ApplicationPage.ApplicationBar.MenuItems.Add(roadMode);
                    ApplicationPage.ApplicationBar.MenuItems.Add(aerialMode);
                    break;
                case 2:
                    ApplicationPage.ApplicationBar.Buttons.Add(refresh);
                    ApplicationPage.ApplicationBar.Buttons.Add(settings); ;
                    break;
                default:
                    break;
            }

        }

        private void ZoomIn_button_Click(object sender, EventArgs e)
        {
            map.ZoomLevel++;
        }

        private void ZoomOut_button_Click(object sender, EventArgs e)
        {
            map.ZoomLevel--;
        }

        private void Refresh_button_Click(object sender, EventArgs e)
        {
            WebCore.RefreshAll();
        }

        private void Settings_button_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/View/Settings.xaml", UriKind.Relative));
        }

        void WebCore_FacebookDataRecived(object sender, LoginEventArgs e)
        {
            
            vm.GetFriends();
            FL.DataContext = vm.AllFriends.OrderBy(f => f.Name);

            var whiteList = vm.AllFriends.Where(f => f.IsWhiteVisible).ToList();

            WhiteListed.DataContext = whiteList;
            WhiteListed2.DataContext = vm.AllFriends;

            TotalFriends.DataContext = vm.AllFriends;

            vm.GetSearchingAreaOptions();
            var searching = vm.SearchingAreaOptions[0].SearchingArea;
            try
            {
                NearFriends.DataContext = (from f in vm.AllFriends where f.Position.GetDistanceTo(vm.Me.Position) <= searching select f).ToList<Friend>();
            }
            catch { NearFriends.DataContext = new List<Friend>(); }
            vm.GetMe();

            PlaceFriends();
            //PositionBox.Text = "Lat " + vm.Me.Position.Latitude + "; Long" + vm.Me.Position.Longitude;


            SetMyPositionAndArea();
            ReverseGeocodeLocation(new Location(vm.Me.Position.Latitude, vm.Me.Position.Longitude));
#warning Messagebox orribile
            //MessageBox.Show(sender as string);
        }

        void WebCore_FacebookFailure(object sender, LoginEventArgs e)
        {
            MessageBox.Show(sender as string);
            NavigationService.Navigate(new Uri("/View/FacebookLogin.xaml", UriKind.Relative));
        }

        #endregion

    }
}